package com.smartcommunity.controller.facelib;

import com.smartcommunity.bean.entity.User;
import com.smartcommunity.bean.model.Pager;
import com.smartcommunity.bean.model.Result;
import com.smartcommunity.controller.BaseController;
import com.smartcommunity.entity.facelib.FaceLib;
import com.smartcommunity.entity.facelib.FaceLibPerson;
import com.smartcommunity.entity.facelib.FaceLibPersonTag;
import com.smartcommunity.log.interceptor.Log;
import com.smartcommunity.service.facelib.FaceLibService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * @version V1.0
 * @ClassName: FaceLibController
 * @Description: 人脸服务
 * @author: tianzongbing
 * @Date: 2020/02/17
 * @Company:
 */
@RestController
@RequestMapping(value = "/faceLib")
@Api(value = "人脸库服务API", tags = "人脸库管理")
@Slf4j
public class FaceLibController extends BaseController {

    @Autowired
    private FaceLibService faceLibService;

    @Autowired
    private HttpServletRequest request;

    @PostMapping(value = "/list")
    @Log(info = "查询人脸库分页列表",module = "人脸库",function = "查询")
    @ApiOperation(value = "分页查询人脸库", notes = "分页查询人脸库")
    public Result<Pager<FaceLib>> listFaceLib(@Valid @RequestBody FaceLib faceLib) {
        try {
            Assert.notNull(faceLib, "查询参数对象不能为NULL");
            User usr = getCurrentUser();
            Assert.notNull(usr, "Session用户对象不能为NULL");
            faceLib.setShare( usr.isAdmin() ? "0,1": "1");
            faceLib.setCreateUser(usr.getId().toString());

            return Result.ok(faceLibService.listFaceLibs(faceLib));
        } catch (Exception e) {
            log.error("分页查询人脸库错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    @PostMapping(value = "/add")
    @Log(info = "新增人脸库信息",module = "人脸库",function = "新增")
    @ApiOperation(value = "新增人脸库", notes = "新增人脸库，不支持批量操作")
    public Result<Integer> addFaceLib(@Valid @RequestBody FaceLib faceLib) {
        try {
            Assert.notNull(faceLib, "参数对象不能为NULL");
            User usr = getCurrentUser();
            Assert.notNull(usr, "Session用户对象不能为NULL");
            // 获取用户账户，填充参数对象
            faceLib.setCreateUser(usr.getId().toString());

            Assert.hasText(faceLib.getShare(), "参数\"是否共享\"不能为空。");
            Assert.isTrue(isBlank(faceLib.getLibId()), "参数不能含有libId");
            Assert.hasText(faceLib.getType(), "参数\"类型\"不能为空");
            Assert.hasText(faceLib.getLibName(), "参数\"库名称\"不能为空");

            return Result.ok(faceLibService.addFaceLib(faceLib));
        } catch (Exception e) {
            log.error("更新人脸库错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    @PostMapping(value = "/update")
    @Log(info = "更新人脸库信息",module = "人脸库",function = "更新")
    @ApiOperation(value = "更新人脸库", notes = "更新人脸库，不支持批量操作")
    public Result<Integer> updateFaceLib(@Valid @RequestBody FaceLib faceLib) {
        try {
            Assert.notNull(faceLib, "参数对象不能为NULL");
            Assert.hasText(faceLib.getLibId(), "参数libId不能为空");
            Assert.doesNotContain(faceLib.getLibId(), ",", "参数libId不能包含逗号");

            User usr = getCurrentUser();
            userValidate(usr,faceLib.getLibId(), false);

            return Result.ok(faceLibService.updateFaceLib(faceLib) > 0);
        } catch (Exception e) {
            log.error("更新人脸库错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    @PostMapping(value = "/delete")
    @Log(info = "删除人脸库信息",module = "人脸库",function = "删除")
    @ApiOperation(value = "删除人脸库", notes = "删除人脸库，支持非批量操作，多个LibId用英文逗号分隔。且会关联删除所有库中人员信息")
    public Result<Integer> deleteFaceLib(@Valid @RequestBody FaceLib faceLib) {
        try {
            Assert.notNull(faceLib, "参数对象不能为NULL");
            Assert.hasText(faceLib.getLibId(), "参数libId不能为空");

            User usr = getCurrentUser();
            userValidate(usr,faceLib.getLibId(), false);

            return faceLibService.deleteFaceLib(faceLib);
        } catch (Exception e) {
            log.error("删除人脸库错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    //  ------- 人脸库人员信息相关接口 -----
    @GetMapping("/person/flushTag")
    @ApiOperation(value = "清空标签缓存", notes = "为了给/person/list接口提供tagEntityList属性，标签实体tagEntity会默认缓存2分钟以减轻后端压力，再次提供接口主动清空缓存")
    public Result<Integer> flushTagCache() {
        int i = faceLibService.flushCache();
        return Result.ok(i);
    }

    @PostMapping(value = "/person/list")
    @Log(info = "查询人脸库中人员分页列表",module = "人脸库",function = "查询")
    @ApiOperation(value = "分页查询人脸库中人脸信息", notes = "分页查询人脸库中人脸信息。注意：标签实体程序会默认缓存2分钟，如果修改或者删除了标签，建议2分钟缓存失效后再操作。" +
            "；标签搜索示例 1 - 多选或查询（至少有一个tagId是2或3的标签关系）：tagList:[{\"tagId\":\"2,3\"}] " +
            "；标签搜索示例 2 - 多选且查询（至少有一个tagId是2或3的标签关系，并且必须有tagId是5和6的标签关系）：tagList:[{\"tagId\":\"2,3\"},{\"tagId\":\"5\"},{\"tagId\":\"6\"}]")
    public Result<Pager<FaceLibPerson>> listFaceLibPerson(@Valid @RequestBody FaceLibPerson faceLibPerson) {
        try {
            Assert.notNull(faceLibPerson, "参数对象不能为NULL");

            userValidate(getCurrentUser(),faceLibPerson.getLibId(), true);

            return Result.ok(faceLibService.listFaceLibPerson(faceLibPerson));
        } catch (Exception e) {
            log.error("分页查询人脸库中人脸信息错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    @PostMapping(value = "/person/add")
    @Log(info = "新增人脸库中人脸信息",module = "人脸库",function = "新增")
    @ApiOperation(value = "新增人脸库中人脸信息", notes = "新增人脸库中人脸信息，不支持批量操作")
    public Result<Integer> addFaceLibPerson(@Valid @RequestBody FaceLibPerson faceLibPerson) {
        try {
            Assert.notNull(faceLibPerson, "参数对象不能为NULL");
            faceLibPerson.setId(null);
            Assert.hasText((faceLibPerson.getLibId()), "参数libId不能为空");
            Assert.hasText((faceLibPerson.getIdCardNo()), "证件号不能为空");
            Assert.hasText((faceLibPerson.getCredentialType()), "证件类型不能为空");

            Assert.isTrue(faceLibPerson.validateIdCardNo(), "证件号格式错误，请检查证件类型和证件号");
            Assert.isTrue(faceLibPerson.validatePhone(), "电话格式错误");
//            Assert.isTrue(isNotBlank(faceLibPerson.getFaceImageUrl()), "人脸图片不能为空");
//            Assert.notEmpty(faceLibPerson.getFeatures(), "参数人脸特征集合features不能为空");
            Assert.isTrue(Optional.ofNullable(faceLibPerson.getFeatures())
                            .map(s -> s.stream()
                                    .filter(f -> isBlank(f.getFeature()) || isBlank(f.getUrl()))
                                    .count() == 0)
                            .orElse(true)
                    , "人脸图片中不能包含特征码或URL为空的元素");

            userValidate(getCurrentUser(),faceLibPerson.getLibId(), false);

            return Result.ok(faceLibService.addFaceLibPerson(faceLibPerson));
        } catch (Exception e) {
            log.error("新增人脸库中人脸信息错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    @PostMapping(value = "/person/update")
    @Log(info = "更新人脸库中人脸信息",module = "人脸库",function = "更新")
    @ApiOperation(value = "更新人脸库中人脸信息", notes = "更新人脸库中人脸信息，不支持批量操作")
    public Result<Integer> updateFaceLibPerson(@Valid @RequestBody FaceLibPerson faceLibPerson) {
        try {
            // 参数简单校验
            Assert.notNull(faceLibPerson, "参数对象不能为NULL");
            Assert.hasText((faceLibPerson.getId()), "参数Id不能为空");
            Assert.hasText((faceLibPerson.getLibId()), "参数libId不能为空");
            Assert.doesNotContain(faceLibPerson.getId(), ",", "参数Id不能包含英文逗号");
            Assert.doesNotContain(faceLibPerson.getLibId(), ",", "参数libId不能包含英文逗号");
            Assert.isTrue(faceLibPerson.validateIdCardNo(), "证件号格式错误，请检查证件类型和证件号");
            Assert.isTrue(faceLibPerson.validatePhone(), "电话格式错误");
//            Assert.isTrue(isNotBlank(faceLibPerson.getFaceImageUrl()), "人脸图片不能为空");

            Assert.isTrue(Optional.ofNullable(faceLibPerson.getTagList())
                            .map(tags -> tags.stream().map(FaceLibPersonTag::getTagId)
                                    .filter(StringUtils::isBlank).count() == 0
                            ).orElse(true)
                    , "参数中tagList中不能有tagId为空的元素");

            userValidate(getCurrentUser(),faceLibPerson.getLibId(), false);

            return Result.ok(faceLibService.updateFaceLibPerson(faceLibPerson));
        } catch (Exception e) {
            log.error("更新人脸库中人脸信息错误", e);
            String msg = getExceptionMessage(e);
            msg = StringUtils.startsWith(msg,"Duplicate entry")? "证件号已被库中其他人员使用" :msg;
            return Result.error(500, msg);
        }
    }

    @PostMapping(value = "/person/delete")
    @Log(info = "删除人脸库中人脸信息",module = "人脸库",function = "删除")
    @ApiOperation(value = "删除人脸库中人脸信息", notes = "删除人脸库中人脸信息，支持批量操作")
    public Result<Integer> deleteFaceLibPerson(@Valid @RequestBody FaceLibPerson faceLibPerson) {
        try {
            Assert.notNull(faceLibPerson, "参数对象不能为NULL");
            Assert.hasText((faceLibPerson.getId()), "参数Id不能为空");
            Assert.hasText((faceLibPerson.getLibId()), "参数libId不能为空");

            userValidate(getCurrentUser(),faceLibPerson.getLibId(), false);

            return Result.ok(faceLibService.deleteFaceLibPerson(faceLibPerson));
        } catch (Exception e) {
            log.error("删除人脸库中人脸信息错误", e);
            return Result.error(500, getExceptionMessage(e));
        }
    }

    // ------- 工具方法 -------

    /**
     * 校验update/delete等接口的读写权限
     * @param usr 当前用户
     * @param libId 人脸库ID，多个用逗号分隔
     * @param readOnly 是否只读。true-只读；false-可写
     */
    private void userValidate(User usr, String libId, boolean readOnly){
        Assert.notNull(usr, "Session用户对象不能为NULL");
        List<FaceLib> libs = faceLibService.findFaceLibByLibIds(libId);
        Assert.notEmpty(libs, "找不到人脸库对象，LibId:"+libId);

        boolean isCreator = libs
                .stream().map(FaceLib::getCreateUser).map(Integer::valueOf)
                .allMatch(id -> Objects.equals(usr.getId(), id));

        if(readOnly){
            // 可读权限
            boolean share = libs
                    .stream().map(FaceLib::getShare)
                    .allMatch(v -> Objects.equals("1", v));
            Assert.isTrue(usr.isAdmin() || isCreator || share
                    ,"您没有可读权限，LibId:"+libId);
        }else{
            // 可写权限
            Assert.isTrue(usr.isAdmin() || isCreator
                    ,"只有管理员和创建用户才能操作");
        }
    }

    /**
     * 获取根异常的描述信息
     *
     * @param e 异常对象
     * @return 根异常的
     */
    public static String getExceptionMessage(Exception e) {
        return Optional.ofNullable(e)
                .map(ex -> null == ex.getCause() ? ex : ex.getCause())
                .map(ex -> StringUtils.isNotEmpty(ex.getMessage()) ? ex.getMessage() : ex.getClass().getCanonicalName())
                .orElse(StringUtils.EMPTY);
    }
}
